package com.code2roc.xhood.asm;

import com.code2roc.xhood.model.ClassParentInfo;
import com.code2roc.xhood.model.ObsureEnum;
import com.code2roc.xhood.util.CommonUtil;
import com.code2roc.xhood.util.ObscureUtil;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.util.HashMap;
import java.util.List;

public class MethodInvokeAdapter extends MethodVisitor {
    private List<String> refreshList;
    private String outPutPath;

    public MethodInvokeAdapter(MethodVisitor mv, List<String> refreshList, String outPutPath) {
        super(Opcodes.ASM9, mv);
        this.refreshList = refreshList;
        this.outPutPath = outPutPath;
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
        String className = owner.replace("/", ".");
        boolean flag = checkMethod(owner, name, descriptor);
        if (flag) {
            String obfuscatedName = ObscureUtil.getObscureNameV3(className, name, ObsureEnum.Method);
            super.visitMethodInsn(opcode, owner, obfuscatedName, descriptor, isInterface);
        } else {
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }
    }

    @Override
    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
        boolean flag = false;
        String className = "";
        if (name != null && descriptor != null) {
            String owner = descriptor.split("\\)")[1].replace(";", "");
            owner = owner.substring(1, owner.length());
            className = owner.replace("/", ".");
            String des = "";
            for (Object item : bootstrapMethodArguments) {
                if (item instanceof Type) {
                    Type type = (Type) item;
                    des = type.getDescriptor();
                    break;
                }
            }
            if (StringUtils.isNotEmpty(des)) {
                flag = checkMethod(owner, name, des);
            }
        }
        if (flag) {
            String obfuscatedName = ObscureUtil.getObscureNameV3(className, name, ObsureEnum.Method);
            super.visitInvokeDynamicInsn(obfuscatedName, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
        } else {
            super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
        }

    }

    private boolean checkMethod(String owner, String name, String descriptor) {
        String className = owner.replace("/", ".");
        boolean flag = false;
        String methodName = owner.replace("/", ".") + "." + name + "|" + descriptor;
        if (refreshList.contains(methodName)) {
            flag = true;
        } else {
            ClassParentInfo info = ObscureUtil.getClassExtendAndInterfaceInfo(outPutPath, owner.replace("/", "."));
            if (StringUtils.isNotEmpty(info.getSuperName())) {
                HashMap<String, String> paramMap = new HashMap();
                String obfuscatedName = ObscureUtil.getObscureNameV3(className, name, ObsureEnum.Method);
                ObscureUtil.findAllParentMethod(outPutPath, obfuscatedName, descriptor, info.getSuperName(), paramMap);
                for (String interfaceName : info.getInterfaceList()) {
                    ObscureUtil.findAllParentMethod(outPutPath, obfuscatedName, descriptor, interfaceName, paramMap);
                }
                for (String value : paramMap.values()) {
                    String key = value + "." + name + "|" + descriptor;
                    if (refreshList.contains(key)) {
                        flag = true;
                        break;
                    }
                }
            }
        }
        return flag;
    }
}
